The goal here is to create a prediction model for the value of benthic over the Chesepeake bay region.

This prediction model will be graphically portrayed with a:

  1. Heatmap
    1. with base plot
    2. wigh ggplot2 over an actual map
  2. Contour plot
    1. with base plot
    2. With ggplot2 over an actual map
  3. 3d Surface plot
    1. with base plot
    2. with a dynamic plotly surface plot

Preamble

Import Library

The following code will automatically install packages that are not already installed and load them into the library:

if(require('pacman')){
  library('pacman')
}else{
  install.packages('pacman')
  library('pacman')
}
pacman::p_load(sp, EnvStats, gstat, ggplot2, rmarkdown, reshape2, ggmap, RColorBrewer, parallel, dplyr, plotly)

Create Assignments

Note that Benthic.df is included in the EnvStats package and is not a data frame but an sp class, which is very similar to a data frame

#Assignments
benthic <- Benthic.df
lat   <- benthic$Latitude
lon   <- benthic$Longitude
index <- benthic$Index

Inspect the Data Set

head(benthic, 3)

Lattitude and Longitude

Assign the Co-ordinates

The Benthic Data set is an sp class objects and requires for its co-ordinates to be set:

coordinates(benthic) = ~Longitude + Latitude

Preliminary Step: Create a krige prediction Model

In order to create a krige prediction model two things are required:

Create a variogram Model

Create a Variogram

If a 4th degree polynomial model was used to model the benthic values over space, a variogram can be made from that model thusly:

(The quadratic model is a required parameter of the kriging process, in exercise 12.2 a quadratic model will be used, the spacific mathematics of this though is somewhat beyond me)

x <- lon
y <- lat
benthic.vg <- variogram(object = index ~ 
                          x + y + 
                          x^2 + x*y + y^2 + 
                          x^3 + x^2*y + x*y^2 + y^3 +
                          x^4 + x^3*y + x^2*y^2 + x*y^3 + y^4,
                        data = benthic)
head(benthic.vg)
    np       dist     gamma dir.hor dir.ver   id
1 4545 0.02410565 0.8506164       0       0 var1
2 4149 0.06703996 0.9393502       0       0 var1
3 3883 0.11396396 1.1513831       0       0 var1
4 4023 0.16138943 1.4599238       0       0 var1
5 5963 0.20615519 1.5641015       0       0 var1
6 6732 0.24983031 1.8992994       0       0 var1

Fit an exponential model to that variogram

benthic.vg.fit.exp <- fit.variogram(benthic.vg, model=vgm(1,"Exp", 0.5,1))

Thus the variogram model needed for the krige prediction has been ascertained

Create a Grid of Domain values to use as predictor values

Create a sequence of incrementally increasing for the X and Y axis

This will essentially act as the resolution of the overlay, Most monitors are about 140 ppi, hence assuming the plot will be a 9 inch square, about 800 x 800 pixels should look right, but 200 x 200 will be the used due to performance.

xgrid       <-  seq(min(lon), max(lon), length.out = 200)
ygrid       <-  seq(min(lat), max(lat), length.out = 200)

Combine into a domain base

This domain base however has to be a spatial object, this can be acheived by defining co-ordinates in the data frame

xy.surface  <- expand.grid(lon = xgrid, lat = ygrid)
coordinates(xy.surface) = ~lon + lat
head(xy.surface, 3)
class       : SpatialPoints 
features    : 3 
extent      : -77.3157, -75.9, 38.0018, 39.4883  (xmin, xmax, ymin, ymax)
coord. ref. : NA 

Create the Krige prediction object

benthic.pred <- krige(formula = Index ~1,
                           locations =  benthic,
                           newdata = xy.surface,
                           model= benthic.vg.fit.exp)
[using ordinary kriging]
head(benthic.pred)
class       : SpatialPointsDataFrame 
features    : 6 
extent      : -77.3157, -77.28013, 38.0018, 38.0018  (xmin, xmax, ymin, ymax)
coord. ref. : NA 
variables   : 2
names       :        var1.pred,         var1.var 
min values  : 3.12118704456262, 2.29623742979447 
max values  : 3.14485115010402, 2.32162957155164 

Now there is a prediction model, if that prediction model is extrapolated over our domain (xy.surface) the various plots can be created

Part 1: Create a heatmap

In order to create a heatmap, it is first necessary to take the sp prediction object and make it into pixel data, otherwise the heatmap won’t look quite right:

class(benthic.pred)
[1] "SpatialPointsDataFrame"
attr(,"package")
[1] "sp"
gridded(benthic.pred) = TRUE
class(benthic.pred)
[1] "SpatialPixelsDataFrame"
attr(,"package")
[1] "sp"
spplot(benthic.pred["var1.pred"],main="Predictions of Benthic Index - ordinary kriging")

Create it in ggplot2

First it is necessary to create a data frame from the sp object:

benthic.pred.df      <- cbind(benthic.pred@coords, benthic.pred@data)
benthic.pred.df.tidy <- melt(benthic.pred.df)
No id variables; using all as measure variables
head(benthic.pred.df)
ggplot() +
  geom_tile(data = benthic.pred.df, aes(lon, lat, fill = var1.pred)) +
  labs(fill = "Benthic Index") # 

This plot would be easier to read if it had discrete intervals rather than a continuous scale:

benthic.pred.df$bin <- cut(benthic.pred.df$var1.pred,
                            breaks = as.vector(seq(from = 0, to = 5, length.out = 20)))
ggplot() +
  geom_tile(data = benthic.pred.df, aes(lon, lat, fill = bin)) +
  labs(fill = "Benthic Index", 
       title = "Heatmap of Benthic Index Values",
       x = "Longitude",
       y = "Lattitude")

NA

Map overlay

Now by adjusting the colour scale and opacity, this can be used as an overlay for the map from exercise 10.1:

Part 2: Contour Map

In order to view the surface plot, contours can also be used.

Recall from Week 11/Exercise 10, that contour and surface plots work a little differently to ordinary plots.

A domain grid of equally spaced \(x\) and \(y\) values is required and a matrix of \(z\) values that can be overlayed onto that domain grid in order to provide the surface values required to create the surface plot.

The method from Lecture 10/Wk. 11 involved predicting over the square domain of xy.surface, this won’t work here because instead of the predict function, the krige function was used, hence it is necessary to convert the output from the krige function, into the output that would have been given by the predict function, which is the required \(z\) surface matrix.

This can be acheived with the data.matrix function:

z_surface_matrix <- data.matrix(benthic.pred[1])

Base Package

Using base R packages, a contour plot can be created with the contour function:

contour(xgrid, ygrid, z_surface_matrix,
          xlab="-Longitude (degrees West)", ylab="Latitude (degrees North)",
          main = "contour plot of Krige Prediction",
          labcex = 0.8)
  mtext("Krige using a 4th degree polynomial model", cex = 0.5)

Using ggplot2

In order to create a contour plot in ggplot 2, it will be necessary to use ‘tidy’ data, which is a table of data such that each column corresponds to a variable:

rownames(z_surface_matrix) <- xgrid
  colnames(z_surface_matrix) <- ygrid
  benthic.pred.melt <- melt(z_surface_matrix,
                            varnames = c("Longitude", "Latitude"),
                            value.name = "Benthic_Index")
  head(benthic.pred.melt)

Observe that this is another way of getting the data frame that was required in part 1 (benthic.pred.df), it is included here as another method and for the sake of completion, also it matches the method used in week 10/lecture 11.

Now using ggplot2 a contour plot, can be created:

ggplot(data = benthic.pred.melt, aes(x = Longitude,
                                       y = Latitude,
                                       z = Benthic_Index,
                                       colour = ..level..),
         size = 0.5) +
    labs(x = "Longitude", y = 'Latitude',
         title = 'Benthic Index',
         subtitle = "Chesapeake Bay, Maryland",
         caption = "Higher index value is better",
         col = 'Benthic Index', size = 'Benthic Index') + 
    geom_contour(lwd = 1) +
  theme_classic()

Map Overlay

The advantage to using ggplot2, is the map overlay can be taken advantage of:

Map Overlay with Heatmap

This can be combined with the heatmap layer:

  ggmap(ggmap = map) +
    labs(fill = "Benthic Index",
         x = "Longitude",
         y = "Lattitude", 
         title = "Heatmap of Benthic Index") + 
    guides(alpha=FALSE) +
    geom_tile(data = benthic.pred.df, aes(lon, lat, fill = bin, alpha = var1.pred)) +
    geom_contour(data = benthic.pred.melt, aes(x = Longitude,
                                               y = Latitude,
                                               z = Benthic_Index),
                col = 'indianred',
                binwidth = 0.2,
                size = 0.6) +
    scale_fill_manual(values = cols) 

Part 3: Surface Plots

A surface plot allows a three dimensional view of the model

Base

In base package, a surface plot can be made:

persp(xgrid, ygrid, z_surface_matrix,
        xlim = c(-77.3, -75.9),   #the values domain needs to be adjusted above
        ylim = c(38.1, 39.5),          #Make sure to set an appropriate domain
        # zlim = c(0, 6),
        theta = -45, phi = 30, d = 0.5,
        xlab="-Longitude (degrees West)",
        ylab="Latitude (degrees North)",
        zlab="Benthic Index", ticktype = "detailed") %>% invisible()
surface extends beyond the box
  title(main=paste("Surface Plot of Benthic Index", "Loess Smoothing", sep="\n"))   

Plotly

Plotly has the advantage of being fully interactive and not as difficult to constrain within a plot/box area:

p <- plot_ly(x = ygrid, y = xgrid, z = z_surface_matrix) %>%
    add_surface() %>%
    layout(
      title = "Benthic Index in  Chesapeake Bay",
      scene = list(
        xaxis = list(title = "Longitude",
                     range = c(38.2, 38.9)),  #You shouldn't need to edit the
        yaxis = list(title = "Latitude",      #The domain here, do it above
                     range = c(-77, -75.9)),
        zaxis = list(title = "Benthic Index")
      ))
                                                                  
  p

The surface plot really reinforces the fact that the bay has the lowest (i.e. best) benthic index value.

LS0tDQp0aXRsZTogIkV4ZXJjaXNlIDEyLjEiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOiANCiAgICB0b2M6IHllcw0KLS0tDQpUaGUgZ29hbCBoZXJlIGlzIHRvIGNyZWF0ZSBhIHByZWRpY3Rpb24gbW9kZWwgZm9yIA0KdGhlIHZhbHVlIG9mIGJlbnRoaWMgb3ZlciB0aGUgQ2hlc2VwZWFrZSBiYXkgcmVnaW9uLg0KDQpUaGlzIHByZWRpY3Rpb24gbW9kZWwgd2lsbCBiZSBncmFwaGljYWxseSBwb3J0cmF5ZWQgd2l0aCBhOg0KDQoxLiBIZWF0bWFwDQogICAgaSkgd2l0aCBiYXNlIHBsb3QNCiAgICBpaSkgd2lnaCBgZ2dwbG90MmAgb3ZlciBhbiBhY3R1YWwgbWFwDQoyLiBDb250b3VyIHBsb3QNCiAgICBpKSB3aXRoIGJhc2UgcGxvdA0KICAgIGlpKSBXaXRoIGBnZ3Bsb3QyYCBvdmVyIGFuIGFjdHVhbCBtYXANCjMuIDNkIFN1cmZhY2UgcGxvdA0KICAgIGkpIHdpdGggYmFzZSBwbG90DQogICAgaWkpIHdpdGggYSBkeW5hbWljIGBwbG90bHlgIHN1cmZhY2UgcGxvdA0KDQoNCiNQcmVhbWJsZQ0KIyNJbXBvcnQgTGlicmFyeQ0KDQpUaGUgZm9sbG93aW5nIGNvZGUgd2lsbCBhdXRvbWF0aWNhbGx5IGluc3RhbGwgcGFja2FnZXMgdGhhdCBhcmUgDQpub3QgYWxyZWFkeSBpbnN0YWxsZWQgYW5kIGxvYWQgdGhlbSBpbnRvIHRoZSBsaWJyYXJ5Og0KDQpgYGB7cn0NCmlmKHJlcXVpcmUoJ3BhY21hbicpKXsNCiAgbGlicmFyeSgncGFjbWFuJykNCn1lbHNlew0KICBpbnN0YWxsLnBhY2thZ2VzKCdwYWNtYW4nKQ0KICBsaWJyYXJ5KCdwYWNtYW4nKQ0KfQ0KDQpwYWNtYW46OnBfbG9hZChzcCwgRW52U3RhdHMsIGdzdGF0LCBnZ3Bsb3QyLCBybWFya2Rvd24sIHJlc2hhcGUyLCBnZ21hcCwgUkNvbG9yQnJld2VyLCBwYXJhbGxlbCwgZHBseXIsIHBsb3RseSkNCmBgYA0KDQojI0NyZWF0ZSBBc3NpZ25tZW50cw0KDQpOb3RlIHRoYXQgIEJlbnRoaWMuZGYgaXMgaW5jbHVkZWQgaW4gdGhlIGBFbnZTdGF0c2AgcGFja2FnZSBhbmQgaXMgbm90IGEgDQpkYXRhIGZyYW1lIGJ1dCBhbiBgc3BgIGNsYXNzLCB3aGljaCBpcyB2ZXJ5IHNpbWlsYXIgdG8gYSBkYXRhIGZyYW1lDQoNCmBgYHtyfQ0KI0Fzc2lnbm1lbnRzDQpiZW50aGljIDwtIEJlbnRoaWMuZGYNCmxhdCAgIDwtIGJlbnRoaWMkTGF0aXR1ZGUNCmxvbiAgIDwtIGJlbnRoaWMkTG9uZ2l0dWRlDQppbmRleCA8LSBiZW50aGljJEluZGV4DQpgYGANCg0KI0luc3BlY3QgdGhlIERhdGEgU2V0DQoNCmBgYHtyfQ0KDQpoZWFkKGJlbnRoaWMsIDMpDQpgYGANCg0KI0xhdHRpdHVkZSBhbmQgTG9uZ2l0dWRlDQoqIExhdHRpdHVkZSBpcyBhIGxpbmUgdGhhdCBydW5zIGVhc3QgdG8gd2VzdCB0aGF0IG1lYXN1cmVzIGhvdyBmYXIgDQpub3J0aCBvZiB0aGUgZXF1YXRvdXIgYSBsb2NhdGlvbiBpcw0KICAgICAgKyBMYXR0aXR1ZGUgaXMgYXNzaWduZWQgdG8gdGhlIHktYXhpcw0KKiBMb25naXR1ZGUgaXMgYSBsaW5lIHRoYXQgcnVucyBub3J0aCB0byBzb3V0aCB0aGF0IG1lYXN1cmVzIGVhc3Qvd2VzdA0KICAgICArIExvbmdpdHVkZSBpcyBhc3NpZ25lZCB0byB0aGUgeC1heGlzDQoNCg0KDQojQXNzaWduIHRoZSBDby1vcmRpbmF0ZXMNClRoZSAqQmVudGhpYyogRGF0YSBzZXQgaXMgYW4gYHNwYCBjbGFzcyBvYmplY3RzIGFuZCByZXF1aXJlcyBmb3IgaXRzIGNvLW9yZGluYXRlcyB0byBiZSBzZXQ6DQoNCmBgYHtyfQ0KY29vcmRpbmF0ZXMoYmVudGhpYykgPSB+TG9uZ2l0dWRlICsgTGF0aXR1ZGUNCmBgYA0KI1ByZWxpbWluYXJ5IFN0ZXA6IENyZWF0ZSBhIGtyaWdlIHByZWRpY3Rpb24gTW9kZWwNCkluIG9yZGVyIHRvIGNyZWF0ZSBhIGtyaWdlIHByZWRpY3Rpb24gbW9kZWwgdHdvIHRoaW5ncyBhcmUgcmVxdWlyZWQ6DQoNCiogRml0dGVkIFZhcmlvZ3JhbSBNb2RlbCAoZS5nLiBhbiBleHBvbmVudGlhbCBtb2RlbCkNCiogQSBkb21haW4gdmFsdWVzIG92ZXIgd2hpY2ggdG8gcHJlZGljdCB2YWx1ZXMNCg0KIyNDcmVhdGUgYSB2YXJpb2dyYW0gTW9kZWwNCg0KDQoNCg0KIyMjQ3JlYXRlIGEgVmFyaW9ncmFtIA0KSWYgYSA0dGggZGVncmVlIHBvbHlub21pYWwgbW9kZWwgd2FzIHVzZWQgdG8gbW9kZWwgdGhlIA0KYmVudGhpYyB2YWx1ZXMgb3ZlciBzcGFjZSwgYSB2YXJpb2dyYW0gY2FuIGJlIG1hZGUgZnJvbQ0KdGhhdCBtb2RlbCB0aHVzbHk6DQoNCihUaGUgcXVhZHJhdGljIG1vZGVsIGlzIGEgcmVxdWlyZWQgcGFyYW1ldGVyIG9mIHRoZSBrcmlnaW5nIHByb2Nlc3MsDQppbiBleGVyY2lzZSAxMi4yIGEgcXVhZHJhdGljIG1vZGVsIHdpbGwgYmUgdXNlZCwNCnRoZSBzcGFjaWZpYyBtYXRoZW1hdGljcyBvZiB0aGlzIHRob3VnaCBpcyBzb21ld2hhdCBiZXlvbmQgbWUpDQoNCmBgYHtyfQ0KeCA8LSBsb24NCnkgPC0gbGF0DQoNCmJlbnRoaWMudmcgPC0gdmFyaW9ncmFtKG9iamVjdCA9IGluZGV4IH4gDQogICAgICAgICAgICAgICAgICAgICAgICAgIHggKyB5ICsgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHheMiArIHgqeSArIHleMiArIA0KICAgICAgICAgICAgICAgICAgICAgICAgICB4XjMgKyB4XjIqeSArIHgqeV4yICsgeV4zICsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeF40ICsgeF4zKnkgKyB4XjIqeV4yICsgeCp5XjMgKyB5XjQsDQogICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gYmVudGhpYykNCg0KaGVhZChiZW50aGljLnZnKQ0KYGBgDQoNCg0KIyMjRml0IGFuIGV4cG9uZW50aWFsIG1vZGVsIHRvIHRoYXQgdmFyaW9ncmFtDQoNCmBgYHtyfQ0KYmVudGhpYy52Zy5maXQuZXhwIDwtIGZpdC52YXJpb2dyYW0oYmVudGhpYy52ZywgbW9kZWw9dmdtKDEsIkV4cCIsIDAuNSwxKSkNCmBgYA0KDQpUaHVzIHRoZSB2YXJpb2dyYW0gbW9kZWwgbmVlZGVkIGZvciB0aGUga3JpZ2UgcHJlZGljdGlvbiBoYXMgYmVlbiBhc2NlcnRhaW5lZA0KDQojI0NyZWF0ZSBhIEdyaWQgb2YgRG9tYWluIHZhbHVlcyB0byB1c2UgYXMgcHJlZGljdG9yIHZhbHVlcw0KIyMjIENyZWF0ZSBhIHNlcXVlbmNlIG9mIGluY3JlbWVudGFsbHkgaW5jcmVhc2luZyBmb3IgdGhlIFggYW5kIFkgYXhpcw0KVGhpcyB3aWxsIGVzc2VudGlhbGx5IGFjdCBhcyB0aGUgcmVzb2x1dGlvbiBvZiB0aGUgb3ZlcmxheSwNCk1vc3QgbW9uaXRvcnMgYXJlIGFib3V0IDE0MCBwcGksIGhlbmNlIGFzc3VtaW5nIHRoZSBwbG90IHdpbGwgYmUgYQ0KOSBpbmNoIHNxdWFyZSwgYWJvdXQgODAwIHggODAwIHBpeGVscyBzaG91bGQgbG9vayByaWdodCwgYnV0IA0KMjAwIHggMjAwIHdpbGwgYmUgdGhlIHVzZWQgZHVlIHRvIHBlcmZvcm1hbmNlLg0KYGBge3J9DQp4Z3JpZCAgICAgICA8LSAgc2VxKG1pbihsb24pLCBtYXgobG9uKSwgbGVuZ3RoLm91dCA9IDIwMCkNCnlncmlkICAgICAgIDwtICBzZXEobWluKGxhdCksIG1heChsYXQpLCBsZW5ndGgub3V0ID0gMjAwKQ0KYGBgDQoNCg0KIyMjQ29tYmluZSBpbnRvIGEgZG9tYWluIGJhc2UNClRoaXMgZG9tYWluIGJhc2UgaG93ZXZlciBoYXMgdG8gYmUgYSBzcGF0aWFsIG9iamVjdCwgdGhpcyBjYW4gYmUNCmFjaGVpdmVkIGJ5IGRlZmluaW5nIGNvLW9yZGluYXRlcyBpbiB0aGUgZGF0YSBmcmFtZQ0KYGBge3J9DQp4eS5zdXJmYWNlICA8LSBleHBhbmQuZ3JpZChsb24gPSB4Z3JpZCwgbGF0ID0geWdyaWQpDQpjb29yZGluYXRlcyh4eS5zdXJmYWNlKSA9IH5sb24gKyBsYXQNCmhlYWQoeHkuc3VyZmFjZSwgMykNCmBgYA0KDQojI0NyZWF0ZSB0aGUgS3JpZ2UgcHJlZGljdGlvbiBvYmplY3QNCmBgYHtyfQ0KDQpiZW50aGljLnByZWQgPC0ga3JpZ2UoZm9ybXVsYSA9IEluZGV4IH4xLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9jYXRpb25zID0gIGJlbnRoaWMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0geHkuc3VyZmFjZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsPSBiZW50aGljLnZnLmZpdC5leHApDQpoZWFkKGJlbnRoaWMucHJlZCkNCmBgYA0KDQpOb3cgdGhlcmUgaXMgYSBwcmVkaWN0aW9uIG1vZGVsLCBpZiB0aGF0IHByZWRpY3Rpb24gbW9kZWwgaXMNCmV4dHJhcG9sYXRlZCBvdmVyIG91ciBkb21haW4gKGB4eS5zdXJmYWNlYCkgdGhlIHZhcmlvdXMgcGxvdHMgY2FuIGJlDQpjcmVhdGVkDQoNCiNQYXJ0IDE6IENyZWF0ZSBhIGhlYXRtYXANCg0KSW4gb3JkZXIgdG8gY3JlYXRlIGEgaGVhdG1hcCwgaXQgaXMgZmlyc3QgbmVjZXNzYXJ5IHRvIHRha2UNCnRoZSBgc3BgIHByZWRpY3Rpb24gb2JqZWN0IGFuZCBtYWtlIGl0IGludG8gcGl4ZWwgZGF0YSwNCm90aGVyd2lzZSB0aGUgaGVhdG1hcCB3b24ndCBsb29rIHF1aXRlIHJpZ2h0Og0KDQpgYGB7cn0NCmNsYXNzKGJlbnRoaWMucHJlZCkNCmdyaWRkZWQoYmVudGhpYy5wcmVkKSA9IFRSVUUNCmNsYXNzKGJlbnRoaWMucHJlZCkNCmBgYA0KDQoNCmBgYHtyfQ0Kc3BwbG90KGJlbnRoaWMucHJlZFsidmFyMS5wcmVkIl0sbWFpbj0iUHJlZGljdGlvbnMgb2YgQmVudGhpYyBJbmRleCAtIG9yZGluYXJ5IGtyaWdpbmciKQ0KYGBgDQoNCg0KIyNDcmVhdGUgaXQgaW4gZ2dwbG90Mg0KRmlyc3QgaXQgaXMgbmVjZXNzYXJ5IHRvIGNyZWF0ZSBhIGRhdGEgZnJhbWUgZnJvbSB0aGUgYHNwYCBvYmplY3Q6DQoNCmBgYHtyfQ0KYmVudGhpYy5wcmVkLmRmICAgICAgPC0gY2JpbmQoYmVudGhpYy5wcmVkQGNvb3JkcywgYmVudGhpYy5wcmVkQGRhdGEpDQpiZW50aGljLnByZWQuZGYudGlkeSA8LSBtZWx0KGJlbnRoaWMucHJlZC5kZikNCmhlYWQoYmVudGhpYy5wcmVkLmRmKQ0KDQpnZ3Bsb3QoKSArDQogIGdlb21fdGlsZShkYXRhID0gYmVudGhpYy5wcmVkLmRmLCBhZXMobG9uLCBsYXQsIGZpbGwgPSB2YXIxLnByZWQpKSArDQogIGxhYnMoZmlsbCA9ICJCZW50aGljIEluZGV4IikgIyANCmBgYA0KDQoNCg0KVGhpcyBwbG90IHdvdWxkIGJlIGVhc2llciB0byByZWFkIGlmIGl0IGhhZCBkaXNjcmV0ZSBpbnRlcnZhbHMNCnJhdGhlciB0aGFuIGEgY29udGludW91cyBzY2FsZToNCg0KYGBge3J9DQpiZW50aGljLnByZWQuZGYkYmluIDwtIGN1dChiZW50aGljLnByZWQuZGYkdmFyMS5wcmVkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGFzLnZlY3RvcihzZXEoZnJvbSA9IDAsIHRvID0gNSwgbGVuZ3RoLm91dCA9IDIwKSkpDQoNCmdncGxvdCgpICsNCiAgZ2VvbV90aWxlKGRhdGEgPSBiZW50aGljLnByZWQuZGYsIGFlcyhsb24sIGxhdCwgZmlsbCA9IGJpbikpICsNCiAgbGFicyhmaWxsID0gIkJlbnRoaWMgSW5kZXgiLCANCiAgICAgICB0aXRsZSA9ICJIZWF0bWFwIG9mIEJlbnRoaWMgSW5kZXggVmFsdWVzIiwNCiAgICAgICB4ID0gIkxvbmdpdHVkZSIsDQogICAgICAgeSA9ICJMYXR0aXR1ZGUiKQ0KICANCmBgYA0KDQojIyNNYXAgb3ZlcmxheQ0KDQpOb3cgYnkgYWRqdXN0aW5nIHRoZSBjb2xvdXIgc2NhbGUgYW5kIG9wYWNpdHksIHRoaXMgY2FuIGJlIHVzZWQgYXMgYW4gb3ZlcmxheQ0KZm9yIHRoZSBtYXAgZnJvbSBleGVyY2lzZSAxMC4xOg0KDQpgYGB7cn0NCmJlbnRoaWMucHJlZC5kZiRiaW4gPC0gY3V0KGJlbnRoaWMucHJlZC5kZiR2YXIxLnByZWQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYXMudmVjdG9yKHNlcShmcm9tID0gMCwgdG8gPSA1LCBsZW5ndGgub3V0ID0gOSkpKQ0KDQojR2V0IGEgbWFwDQogIGJib3ggPC0gbWFrZV9iYm94KGJlbnRoaWMucHJlZC5kZiRsb24sIGJlbnRoaWMucHJlZC5kZiRsYXQsIGYgPSAwLjAxKQ0KICBtYXAgPC0gZ2V0X21hcChsb2NhdGlvbiA9IGJib3gsIG1hcHR5cGUgPSAndG9uZXInKQ0KICAjQ3JlYXRlIHRoZSBjb2xvdXJzDQogIGNvbHMgPC0gYnJld2VyLnBhbChuID0gbGVuZ3RoKGxldmVscyhiZW50aGljLnByZWQuZGYkYmluKSksIG5hbWUgPSAiT3JhbmdlcyIpIA0KICAjQ3JlYXRlIHRoZSBQbG90DQogIGdnbWFwKGdnbWFwID0gbWFwKSArDQogIGdlb21fdGlsZShkYXRhID0gYmVudGhpYy5wcmVkLmRmLCBhZXMobG9uLCBsYXQsIGZpbGwgPSBiaW4sIGFscGhhID0gdmFyMS5wcmVkKSkrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbHMpICsNCiAgbGFicyhmaWxsID0gIkJlbnRoaWMgSW5kZXgiLA0KICAgICAgIHggPSAiTG9uZ2l0dWRlIiwNCiAgICAgICB5ID0gIkxhdHRpdHVkZSIsIA0KICAgICAgIHRpdGxlID0gIkhlYXRtYXAgb2YgQmVudGhpYyBJbmRleCIpICsgDQogIGd1aWRlcyhhbHBoYT1GQUxTRSkgDQogICAgDQpgYGANCg0KDQojUGFydCAyOiBDb250b3VyIE1hcA0KSW4gb3JkZXIgdG8gdmlldyB0aGUgc3VyZmFjZSBwbG90LCBjb250b3VycyBjYW4gYWxzbyBiZSB1c2VkLg0KDQogUmVjYWxsIGZyb20gV2VlayAxMS9FeGVyY2lzZSAxMCwgdGhhdCBjb250b3VyIGFuZCBzdXJmYWNlIHBsb3RzDQogIHdvcmsgYSBsaXR0bGUgZGlmZmVyZW50bHkgdG8gb3JkaW5hcnkgcGxvdHMuDQogIA0KICBBIGRvbWFpbiBncmlkIG9mIGVxdWFsbHkgc3BhY2VkICR4JCBhbmQgJHkkIHZhbHVlcyBpcyByZXF1aXJlZCBhbmQNCiAgYSBtYXRyaXggb2YgJHokIHZhbHVlcyB0aGF0IGNhbiBiZSBvdmVybGF5ZWQgb250byB0aGF0IGRvbWFpbiBncmlkDQogIGluIG9yZGVyIHRvIHByb3ZpZGUgdGhlIHN1cmZhY2UgdmFsdWVzIHJlcXVpcmVkIA0KICB0byBjcmVhdGUgdGhlIHN1cmZhY2UgcGxvdC4NCg0KDQoNCiAgVGhlIG1ldGhvZCBmcm9tIExlY3R1cmUgMTAvV2suIDExIGludm9sdmVkIHByZWRpY3Rpbmcgb3Zlcg0KICB0aGUgc3F1YXJlIGRvbWFpbiBvZiBgeHkuc3VyZmFjZWAsIHRoaXMgd29uJ3Qgd29yayBoZXJlDQogIGJlY2F1c2UgaW5zdGVhZCBvZiB0aGUgYHByZWRpY3RgIGZ1bmN0aW9uLCB0aGUgYGtyaWdlYCBmdW5jdGlvbg0KICB3YXMgdXNlZCwgaGVuY2UgaXQgaXMgbmVjZXNzYXJ5IHRvIGNvbnZlcnQgdGhlIG91dHB1dCBmcm9tDQogIHRoZSBga3JpZ2VgIGZ1bmN0aW9uLCBpbnRvIHRoZSBvdXRwdXQgdGhhdCB3b3VsZCBoYXZlIGJlZW4gDQogIGdpdmVuIGJ5IHRoZSBwcmVkaWN0IGZ1bmN0aW9uLCB3aGljaCBpcyB0aGUgcmVxdWlyZWQNCiAgJHokIHN1cmZhY2UgbWF0cml4Lg0KICANCiAgVGhpcyBjYW4gYmUgYWNoZWl2ZWQgd2l0aCB0aGUgYGRhdGEubWF0cml4YCBmdW5jdGlvbjoNCiAgDQogIA0KYGBge3J9DQp6X3N1cmZhY2VfbWF0cml4IDwtIGRhdGEubWF0cml4KGJlbnRoaWMucHJlZFsxXSkNCmBgYA0KDQojI0Jhc2UgUGFja2FnZQ0KVXNpbmcgYmFzZSBSIHBhY2thZ2VzLCBhIGNvbnRvdXIgcGxvdCBjYW4gYmUgY3JlYXRlZCB3aXRoIHRoZSBgY29udG91cmANCmZ1bmN0aW9uOg0KYGBge3J9DQpjb250b3VyKHhncmlkLCB5Z3JpZCwgel9zdXJmYWNlX21hdHJpeCwNCiAgICAgICAgICB4bGFiPSItTG9uZ2l0dWRlIChkZWdyZWVzIFdlc3QpIiwgeWxhYj0iTGF0aXR1ZGUgKGRlZ3JlZXMgTm9ydGgpIiwNCiAgICAgICAgICBtYWluID0gImNvbnRvdXIgcGxvdCBvZiBLcmlnZSBQcmVkaWN0aW9uIiwNCiAgICAgICAgICBsYWJjZXggPSAwLjgpDQogIG10ZXh0KCJLcmlnZSB1c2luZyBhIDR0aCBkZWdyZWUgcG9seW5vbWlhbCBtb2RlbCIsIGNleCA9IDAuNSkNCmBgYA0KDQojI1VzaW5nIGdncGxvdDINCkluIG9yZGVyIHRvIGNyZWF0ZSBhIGNvbnRvdXIgcGxvdCBpbiBnZ3Bsb3QgMiwNCml0IHdpbGwgYmUgbmVjZXNzYXJ5IHRvIHVzZSAndGlkeScgZGF0YSwgd2hpY2ggaXMgDQphIHRhYmxlIG9mIGRhdGEgc3VjaCB0aGF0IGVhY2ggY29sdW1uIGNvcnJlc3BvbmRzDQp0byBhIHZhcmlhYmxlOg0KYGBge3J9DQpyb3duYW1lcyh6X3N1cmZhY2VfbWF0cml4KSA8LSB4Z3JpZA0KICBjb2xuYW1lcyh6X3N1cmZhY2VfbWF0cml4KSA8LSB5Z3JpZA0KICBiZW50aGljLnByZWQubWVsdCA8LSBtZWx0KHpfc3VyZmFjZV9tYXRyaXgsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFybmFtZXMgPSBjKCJMb25naXR1ZGUiLCAiTGF0aXR1ZGUiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZS5uYW1lID0gIkJlbnRoaWNfSW5kZXgiKQ0KICBoZWFkKGJlbnRoaWMucHJlZC5tZWx0KQ0KYGBgDQogIE9ic2VydmUgdGhhdCB0aGlzIGlzIGFub3RoZXIgd2F5IG9mIGdldHRpbmcgdGhlDQogIGRhdGEgZnJhbWUgdGhhdCB3YXMgcmVxdWlyZWQgaW4gcGFydCAxIChgYmVudGhpYy5wcmVkLmRmYCksDQogIGl0IGlzIGluY2x1ZGVkIGhlcmUgYXMgYW5vdGhlciBtZXRob2QgYW5kIGZvciB0aGUgc2FrZSBvZiBjb21wbGV0aW9uLA0KICBhbHNvIGl0IG1hdGNoZXMgdGhlIG1ldGhvZCB1c2VkIGluIHdlZWsgMTAvbGVjdHVyZSAxMS4NCg0KDQpOb3cgdXNpbmcgZ2dwbG90MiBhIGNvbnRvdXIgcGxvdCwgY2FuIGJlIGNyZWF0ZWQ6DQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBiZW50aGljLnByZWQubWVsdCwgYWVzKHggPSBMb25naXR1ZGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gTGF0aXR1ZGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB6ID0gQmVudGhpY19JbmRleCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IC4ubGV2ZWwuLiksDQogICAgICAgICBzaXplID0gMC41KSArDQogICAgbGFicyh4ID0gIkxvbmdpdHVkZSIsIHkgPSAnTGF0aXR1ZGUnLA0KICAgICAgICAgdGl0bGUgPSAnQmVudGhpYyBJbmRleCcsDQogICAgICAgICBzdWJ0aXRsZSA9ICJDaGVzYXBlYWtlIEJheSwgTWFyeWxhbmQiLA0KICAgICAgICAgY2FwdGlvbiA9ICJIaWdoZXIgaW5kZXggdmFsdWUgaXMgYmV0dGVyIiwNCiAgICAgICAgIGNvbCA9ICdCZW50aGljIEluZGV4Jywgc2l6ZSA9ICdCZW50aGljIEluZGV4JykgKyANCiAgICBnZW9tX2NvbnRvdXIobHdkID0gMSkgKw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQojIyNNYXAgT3ZlcmxheQ0KVGhlIGFkdmFudGFnZSB0byB1c2luZyBnZ3Bsb3QyLCBpcyB0aGUgbWFwIG92ZXJsYXkgY2FuIGJlIHRha2VuIGFkdmFudGFnZSBvZjoNCg0KDQpgYGB7cn0NCiAgbWFwIDwtIGdldF9tYXAobG9jYXRpb24gPSBiYm94LCBtYXB0eXBlID0gJ3dhdGVyY29sb3InKQ0KICANCiAgZ2dtYXAoZ2dtYXAgPSBtYXApICsNCiAgICBsYWJzKGZpbGwgPSAiQmVudGhpYyBJbmRleCIsDQogICAgICAgICB4ID0gIkxvbmdpdHVkZSIsDQogICAgICAgICB5ID0gIkxhdHRpdHVkZSIsIA0KICAgICAgICAgdGl0bGUgPSAiSGVhdG1hcCBvZiBCZW50aGljIEluZGV4IikgKyANCiAgICBndWlkZXMoYWxwaGE9RkFMU0UpICsNCiAgICBnZW9tX2NvbnRvdXIoZGF0YSA9IGJlbnRoaWMucHJlZC5tZWx0LCBhZXMoeCA9IExvbmdpdHVkZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IExhdGl0dWRlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB6ID0gQmVudGhpY19JbmRleCksDQogICAgICAgICAgICAgICAgIGNvbCA9ICdncmV5NDAnLA0KICAgICAgICAgICAgICAgICBiaW53aWR0aCA9IDAuMTUsDQogICAgICAgICAgICAgICAgIHNpemUgPSAwLjcwKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29scykgDQpgYGANCg0KIyMjTWFwIE92ZXJsYXkgd2l0aCBIZWF0bWFwDQpUaGlzIGNhbiBiZSBjb21iaW5lZCB3aXRoIHRoZSBoZWF0bWFwIGxheWVyOg0KDQpgYGB7cn0NCiAgZ2dtYXAoZ2dtYXAgPSBtYXApICsNCiAgICBsYWJzKGZpbGwgPSAiQmVudGhpYyBJbmRleCIsDQogICAgICAgICB4ID0gIkxvbmdpdHVkZSIsDQogICAgICAgICB5ID0gIkxhdHRpdHVkZSIsIA0KICAgICAgICAgdGl0bGUgPSAiSGVhdG1hcCBvZiBCZW50aGljIEluZGV4IikgKyANCiAgICBndWlkZXMoYWxwaGE9RkFMU0UpICsNCiAgICBnZW9tX3RpbGUoZGF0YSA9IGJlbnRoaWMucHJlZC5kZiwgYWVzKGxvbiwgbGF0LCBmaWxsID0gYmluLCBhbHBoYSA9IHZhcjEucHJlZCkpICsNCiAgICBnZW9tX2NvbnRvdXIoZGF0YSA9IGJlbnRoaWMucHJlZC5tZWx0LCBhZXMoeCA9IExvbmdpdHVkZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IExhdGl0dWRlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB6ID0gQmVudGhpY19JbmRleCksDQogICAgICAgICAgICAgICAgY29sID0gJ2luZGlhbnJlZCcsDQogICAgICAgICAgICAgICAgYmlud2lkdGggPSAwLjIsDQogICAgICAgICAgICAgICAgc2l6ZSA9IDAuNikgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbHMpIA0KYGBgDQoNCiNQYXJ0IDM6IFN1cmZhY2UgUGxvdHMNCkEgc3VyZmFjZSBwbG90IGFsbG93cyBhIHRocmVlIGRpbWVuc2lvbmFsIHZpZXcgb2YgdGhlIG1vZGVsDQoNCiMjQmFzZQ0KSW4gYmFzZSBwYWNrYWdlLCBhIHN1cmZhY2UgcGxvdCBjYW4gYmUgbWFkZToNCmBgYHtyfQ0KcGVyc3AoeGdyaWQsIHlncmlkLCB6X3N1cmZhY2VfbWF0cml4LA0KICAgICAgICB4bGltID0gYygtNzcuMywgLTc1LjkpLCAgICN0aGUgdmFsdWVzIGRvbWFpbiBuZWVkcyB0byBiZSBhZGp1c3RlZCBhYm92ZQ0KICAgICAgICB5bGltID0gYygzOC4xLCAzOS41KSwgICAgICAgICAgI01ha2Ugc3VyZSB0byBzZXQgYW4gYXBwcm9wcmlhdGUgZG9tYWluDQogICAgICAgICMgemxpbSA9IGMoMCwgNiksDQogICAgICAgIHRoZXRhID0gLTQ1LCBwaGkgPSAzMCwgZCA9IDAuNSwNCiAgICAgICAgeGxhYj0iLUxvbmdpdHVkZSAoZGVncmVlcyBXZXN0KSIsDQogICAgICAgIHlsYWI9IkxhdGl0dWRlIChkZWdyZWVzIE5vcnRoKSIsDQogICAgICAgIHpsYWI9IkJlbnRoaWMgSW5kZXgiLCB0aWNrdHlwZSA9ICJkZXRhaWxlZCIpDQogIHRpdGxlKG1haW49cGFzdGUoIlN1cmZhY2UgUGxvdCBvZiBCZW50aGljIEluZGV4IiwgIkxvZXNzIFNtb290aGluZyIsIHNlcD0iXG4iKSkgICANCmBgYA0KDQojI1Bsb3RseQ0KUGxvdGx5IGhhcyB0aGUgYWR2YW50YWdlIG9mIGJlaW5nIGZ1bGx5IGludGVyYWN0aXZlIGFuZCBub3QgYXMgZGlmZmljdWx0IHRvIA0KY29uc3RyYWluIHdpdGhpbiBhIHBsb3QvYm94IGFyZWE6DQpgYGB7cn0NCnAgPC0gcGxvdF9seSh4ID0geWdyaWQsIHkgPSB4Z3JpZCwgeiA9IHpfc3VyZmFjZV9tYXRyaXgpICU+JQ0KICAgIGFkZF9zdXJmYWNlKCkgJT4lDQogICAgbGF5b3V0KA0KICAgICAgdGl0bGUgPSAiQmVudGhpYyBJbmRleCBpbiAgQ2hlc2FwZWFrZSBCYXkiLA0KICAgICAgc2NlbmUgPSBsaXN0KA0KICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiTG9uZ2l0dWRlIiwNCiAgICAgICAgICAgICAgICAgICAgIHJhbmdlID0gYygzOC4yLCAzOC45KSksICAjWW91IHNob3VsZG4ndCBuZWVkIHRvIGVkaXQgdGhlDQogICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJMYXRpdHVkZSIsICAgICAgI1RoZSBkb21haW4gaGVyZSwgZG8gaXQgYWJvdmUNCiAgICAgICAgICAgICAgICAgICAgIHJhbmdlID0gYygtNzcsIC03NS45KSksDQogICAgICAgIHpheGlzID0gbGlzdCh0aXRsZSA9ICJCZW50aGljIEluZGV4IikNCiAgICAgICkpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgcA0KYGBgDQoNCg0KDQpUaGUgc3VyZmFjZSBwbG90IHJlYWxseSByZWluZm9yY2VzIHRoZSBmYWN0IHRoYXQgdGhlIGJheSBoYXMNCnRoZSBsb3dlc3QgKGkuZS4gYmVzdCkgYmVudGhpYyBpbmRleCB2YWx1ZS4NCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=